package com.blue.code.db.src.pdm;

import java.io.File;
import java.io.IOException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import com.blue.code.db.src.Parser;
import com.blue.code.db.src.Table;
import com.blue.code.db.src.TableColumn;
import com.blue.code.db.src.TableId;

/**
 * 从PowerDesigner解析
 * 
 * @author zhengj
 * @since 1.0 2017年1月19日
 */
public class PowerDesignerParser implements Parser
{
	private static Logger logger = LoggerFactory.getLogger(PowerDesignerParser.class);
	
	@Value("${pdm.src}")
	private String src;
	@Value("${table.names}")
	private String tables;
	@Value("${table.prefix.trim}")
	private boolean prefix;
	
	public PowerDesignerParser()
	{
	}

	@Override
	public List<Table> parse()
	{
		Set<String> tableSet = new HashSet<>();
		if (tables != null && !tables.isEmpty())
		{
			for (String table : tables.split(","))
			{
				tableSet.add(table);
			}
		}
		
		Element tableNode = this.getTableNode();
		List<Table> tableList = this.parseTable(tableNode, tableSet);
		return tableList;
	}
	
	private List<Table> parseTable(Element tableNode, Set<String> tableSet)
	{
		List<Table> tableList = new ArrayList<>();
		for (Element tNode : tableNode.getElementsByTag("o:Table"))
		{
			String id = tNode.attr("Id");
			String tableName = null;
			String name = null;
			String comment = null;
			for (Element taNode : tNode.children())
			{
				if ("a:code".equals(taNode.tagName()))
					tableName = taNode.text();
				else if ("a:name".equals(taNode.tagName()))
					name = taNode.text();
				else if ("a:comment".equals(taNode.tagName()))
					comment = taNode.text();
			}
			if (!this.checkTable(tableName, tableSet))
				continue;

			Table table = new Table(tableName);
			tableList.add(table);
			table.setId(id);
			table.setName(name);
			table.setComment(comment);
			
			this.parseColumn(tNode, table);
		}
		
		return tableList;
	}
	
	private boolean checkTable(String tableName, Set<String> tableSet)
	{
		if (tableSet.isEmpty())
			return true;
		
		for (String table : tableSet)
		{
			if (tableName.startsWith(table))
				return true;
		}
		return false;
	}
	
	private void parseColumn(Element tNode, Table table)
	{
		Map<String, Set<String>> kMap = new HashMap<>();
		Set<String> pkSet = new HashSet<>();
		for (Element kNode : tNode.getElementsByTag("o:Key"))
		{
			String id = kNode.attr("Id");
			Elements coNodes = kNode.getElementsByTag("o:Column");
			if (coNodes.size() == 0)
				continue;
			
			Set<String> kSet = new HashSet<>();
			for (Element coNode : coNodes)
			{
				kSet.add(coNode.attr("Ref"));
			}
			kMap.put(id, kSet);
		}
		for (Element pkNode : tNode.getElementsByTag("c:PrimaryKey"))
		{
			Element kNode = pkNode.getElementsByTag("o:Key").first();
			String id = kNode.attr("Ref");
			Set<String> refId = kMap.get(id);
			if (refId != null && !refId.isEmpty())
			{
				pkSet.addAll(refId);
			}
		}
		
		List<TableId> idList = new ArrayList<>();
		List<TableColumn> columnList = new ArrayList<>();
		Element coNodes = tNode.getElementsByTag("c:Columns").first();
		for (Element coNode : coNodes.getElementsByTag("o:Column"))
		{
			String id = coNode.attr("Id");
			String name = this.getNodeText(coNode, "a:Name");
			String code = this.getNodeText(coNode, "a:Code");
			String typeName = this.getNodeText(coNode, "a:DataType");
			String comment = this.getNodeText(coNode, "a:Comment");
			String nullable = this.getNodeText(coNode, "a:Column.Mandatory");
			String size = this.getNodeText(coNode, "a:Length");
			String def = this.getNodeText(coNode, "a:DefaultValue");
			String inc = this.getNodeText(coNode, "a:Identity");
			
			TableColumn column = null;
			if (pkSet.contains(id))
			{
				TableId tableId = new TableId();
				idList.add(tableId);
				tableId.setInc("1".equals(inc));
				column = tableId;
			}
			else
			{
				column = new TableColumn();
				columnList.add(column);
			}
			column.setId(id);
			column.setName(name);
			column.setColumn(code);
			column.setComment(comment);
			column.setDef(def);
			column.setNullable(!"1".equals(nullable));
			column.setTypeName(typeName);
			column.setType(this.getType(typeName));
			if (size != null && StringUtils.isNumeric(size))
				column.setSize(Integer.parseInt(size));
			
		}
		table.setIdList(idList);
		table.setColumnList(columnList);
	}
	
	private String getNodeText(Element node, String tagName)
	{
		Element n = node.getElementsByTag(tagName).first();
		if (n == null)
			return null;
		
		return n.text();
	}

	private Element getTableNode()
	{
		try
		{
			File file = new File(src);
			Document doc = Jsoup.parse(file, "UTF-8");
			Element tableNode = doc.getElementsByTag("c:Tables").first();
			return tableNode;
		}
		catch (IOException e)
		{
			logger.error("读取文件出错：" + src, e);
			throw new PowerDesignerException("读取文件出错：" + src);
		}
	}
	
	private int getType(String typeName)
	{
		int type = Types.VARCHAR;
		if ("bigint".equals(typeName))
			type = Types.BIGINT;
		else if (typeName.contains("int"))
			type = Types.INTEGER;
		else if (typeName.startsWith("numeric") || "double".equals(typeName))
			type = Types.DOUBLE;
		else if ("float".equals(typeName))
			type = Types.FLOAT;
		else if ("timestamp".equals(typeName) || "datetime".equals(typeName))
			type = Types.TIMESTAMP;
		else if ("time".equals(typeName))
			type = Types.TIME;
		else if ("date".equals(typeName))
			type = Types.DATE;
		
		return type;
	}
	
}
